﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using devtm.Editor.TypeSelector;
using Microsoft.VisualStudio.Modeling;
using System.Reflection;
using DslModeling = global::Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
using System.IO;
using System.Diagnostics;
using System.Collections;

namespace devtm.AutoMapper
{


    public class ReferenceTypeProvider : ITypeProvider
    {

        public ReferenceTypeProvider()
        {
            SourceTypes.Source.Add(this);
        }

        public ReferenceTypeProvider(DslModeling.Store store) :  this()
        {
            
            //StoreHostingProject.EnsureProjectReferencesAssembly(store, typeof(System.Configuration.ConfigurationElement).Assembly);

        }



        public static Hashtable CashAssemblies = new Hashtable();

        public IEnumerable<Type> GetTypes(DslModeling.Store store, HashSet<String> namespaces)
        {

            var lst = StHostingProject.ProjectReferencesAssemblies(store).ToList();

            foreach (Assembly item in lst)
            {
                if (item == null)
                    continue;

                Type[] l = null;

                if (CashAssemblies.ContainsKey(item))
                    l = CashAssemblies[item] as Type[];

                else
                {

                    try
                    {
                        l = item.GetTypes();
                        CashAssemblies.Add(item, l);
                    }
                    catch (System.Reflection.ReflectionTypeLoadException e)
                    {

                        var ll = e.LoaderExceptions.Select(c => c.Message).Distinct();
                        foreach (string mm in ll)
                            //    System.Windows.MessageBox.Show(mm);                    
                            Debug.WriteLine(string.Format(@"Can't be parsed type from the the {0} assembly beacause : {1}", item.FullName, mm));

                        l = e.Types.Where(c => c != null).ToArray();

                        CashAssemblies.Add(item, l);

                    }
                }

                if (l != null)
                    foreach (Type type in l.Where(c => c.IsPublic && (namespaces == null || namespaces.Contains(c.Namespace))))
                        yield return type;
            }


            //foreach (devtm.AutoMapper.CustomCode.Dsl2Dte.ReferenceError item in StHostingProject.Errors)
            //{
            //    string d = string.Format("Error on the reference at References Assemblies {0} in the project {1}", item.Location, item.Source);
            //    ContextTypeError ctx = new ContextTypeError() { Context = item.Context, Type = item.GetType(), Description = d, Provider = this, Exception = item.Exception };
            //    SourceTypes.Errors.Add(ctx);
            //}


        }



        public IEnumerable<Type> GetTypes(DslModeling.Store store, HashSet<String> namespaces, Func<Type, bool> filter)
        {

            if (store == null)
                yield break;

            var i = StHostingProject.ProjectReferencesAssemblies(store).ToList();

            if (i != null)
                foreach (Assembly item in i)
                {

                    foreach (Type type in item.GetTypes().Where(c => namespaces == null || namespaces.Contains(c.Namespace)))
                    {
                        
                        if (filter(type))
                            yield return type;

                    }


                }


            //foreach (devtm.AutoMapper.CustomCode.Dsl2Dte.ReferenceError item in StHostingProject.Errors)
            //{
            //    ContextTypeError ctx = new ContextTypeError() { Type = item.GetType(), Description = item.Location, Provider = this };
            //    SourceTypes.Errors.Add(ctx);

            //}



        }



        #region Disposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~ReferenceTypeProvider()
        {
            Dispose(false);
        }

        // The bulk of the clean-up code is implemented in Dispose(bool)
        protected virtual void Dispose(bool disposing)
        {

            if (disposing)
            {
                // free managed resources
            
            }

            SourceTypes.Source.Remove(this);

        }

        #endregion

    }
}
